Next | Prev | Up | Top | Contents | Index

How the Host Adapter Functions Are Found

A SCSI device driver can be asked to manage devices on different adapters. But different adapters can use different hardware, and be managed by different host adapter drivers. When opening one device, the device driver might need to call scsi_alloc() as provided by the wd93 driver. When opening a different device, the driver might need the scsi_alloc() function from the jag driver. How can a driver locate the correct host adapter function for a given device?

The answer is provided by a set of function vector tables that are indexed by adapter number, and that yield the address of the appropriate function for that adapter.


Using the Function Vector Tables

The function vector tables are maintained by the scsi driver module and filled in by each host adapter driver as it is initialized. The four vector tables are declared in sys/scsi.h. The declaration of table scsi_command is as follows:

extern void (*scsi_command[])(struct scsi_request *req);

This declaration states that scsi_command is an array of pointers to functions. Each function has the prototype

void function(struct scsi_request *req);

The four tables scsi_command, scsi_alloc, scsi_free, and scsi_info are similar. Each is an array of pointers to functions. Each array is indexed by the adapter type number. If iAdapT is an integer variable containing the adapter type number for a device, the following statements are valid calls to the four host adapter functions (the function arguments are examined in detail in the following topics):

#include <sys/scsi.h>
pTargInfo = (*scsi_info[iAdapT])(iAdap,iTarg,iLun);
iAllocRet = (*scsi_alloc[iAdapT])(iAdap,iTarg,iLun,0,NULL);
(void) (*scsi_command[iAdapT])(&request);
(void) (*scsi_free[iAdapT])(iAdap,iTarg,iLun,NULL);
Each statement is a function call, but in each case, the name of the function is replaced by an expression that indexes the appropriate table.


Learning the Adapter Type Number

Clearly, a SCSI driver needs to know the adapter type number for each device that it manages. Otherwise it cannot call the host adapter functions to manage that device.

The adapter type number for each adapter in the system is stored in an array maintained by the scsi driver. The array is declared as follows in sys/scsi.h:

extern u_char scsi_driver_table[];

When indexed by the number of the adapter in use, this table returns the adapter type number of the host adapter driver for that adapter.


Learning the Adapter Number

Now all that remains is for the device driver to learn the adapter number with each device that it manages. There are two simple ways to do this.

One method is to get the number in the edt_t structure. When a device is configured using a VECTOR line, the VECTOR should contain an adapter=n parameter. This number is stored in the e_adap field of the edt_t structure that is passed to the pfxedtinit() entry point. Code to retrieve it in a hypothetical driver is shown in Example 15-1.

Example 15-1 : Storing the Adapter Type Number in pfxedtinit()

#include <sys/scsi.h>
typedef struct devVital_s {
   uchar devAdapNum;
   uchar devAdapType;
...} devVital_t;
void hypo_edtinit(edt_t *edt)
{
   devVital_t *pVitals;
   ...
   pVitals->devAdapNum = edt->e_adap;
   pVitals->devAdapType = scsi_driver_table[edt->e_adap];
...
}
A second method is to get it from the device minor number. For all Silicon Graphics disk and tape devices, the adapter number is encoded into both the visible name and the minor number of the device special file. You can use the bits of the minor number of any device in a similar way (see "Minor Device Number").

Under the second plan, geteminor() is used to extract the minor number is extracted from the dev_t value passed to each entry point (see "Device Number Functions"). The adapter number is calculated by shifting and masking the minor number. Hypothetical example code is shown in Example 15-2. The code of Example 15-2 can be extended to macros for the logical unit and control unit in obvious ways.

Example 15-2 : Extracting an Adapter Number From a Minor Device Number

/* Hypothetical minor bits: 00 aaaaaaaa ccccuuuu */
#define MINOR_ADAP_SHIFT 8
#define MINOR_ADAP_MASK 0x00ff
#define MINOR_ADAP(devt) (MINOR_ADAP_MASK & \
                  (geteminor(devt) >> MINOR_ADAP_SHIFT))
When the adapter number is known, the expression to call a host adapter function can be converted to a macro as well, possibly making the code more readable. The macro in Example 15-3 encapsulates a call to scsi_alloc(). This code takes advantage of the fact that the adapter number is an argument to the function in any case.

Example 15-3 : Macro to Encapsulate a Call to scsi_alloc()

#define SCSI_ALLOC(adap,targ,lun,opt,func) \
   (*scsi_alloc[scsi_driver_table[adap]]) \
   (adap,targ,lun,opt,func)
It could be argued that the double indexing in Example 15-3 imposes needless overhead. An approach with minimum overhead is to reserve space in the device-information structure for four function addresses, and to store the addresses of the host adapter functions with the other unique device information when the device is initialized.


Next | Prev | Up | Top | Contents | Index